﻿using CoachingOrganizer.Logic.Common;
using CoachingOrganizer.Logic.Models.Tools.MoneyCalculation;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace CoachingOrganizer.Logic.Logic.Tools.MoneyCalculation
{
    internal class PeriodReportLogic
    {
        private readonly PeriodReportModel _model;
        private readonly LogicExtender _logicExtender;
        private readonly ReportLogic _upperLogic;

        public PeriodReportModel Model
        {
            get { return _model; }
        }

        public PeriodReportLogic(DateTime commonStart, DateTime commonFinish, TimeSpan period, int index, LogicExtender logicExtender, ReportLogic upperLogic)
            : this(new PeriodReportModel(commonStart, commonFinish, period, index, GetStartDate(commonStart, commonFinish, period, index), GetFinishDate(commonStart, commonFinish, period, index)), logicExtender, upperLogic)
        {

        }

        public PeriodReportLogic(PeriodReportModel model, LogicExtender logicExtender, ReportLogic upperLogic)
        {
            _model = model;
            _logicExtender = logicExtender;
            _upperLogic = upperLogic;
            model.EnumerateIncomesGroupsDelegate = EnumerateIncomesGroups;
            model.EnumerateSpentsGroupsDelegate = EnumerateSpentsGroups;
            model.GetTotalIncomesDelegate = GetTotalIncomes;
            model.GetTotalSpentsDelegate = GetTotalSpents;
        }

        public decimal GetTotalIncomes()
        {
            return _upperLogic.UpperLogic.Model.Incomes.StableItems.Select(i => i.Amount).Union(_upperLogic.UpperLogic.Model.Incomes.OccasionalItems.Select(i => i.Amount)).Sum();
        }

        public decimal GetTotalSpents()
        {
            return _upperLogic.UpperLogic.Model.Spents.StableItems.Select(i => i.Amount).Union(_upperLogic.UpperLogic.Model.Spents.OccasionalItems.Select(i => i.Amount)).Sum();
        }

        public IEnumerable<PeriodGroupReportLogicBase> EnumerateIncomesGroups()
        {
            return EnumerateStableIncomesGroups().Cast<PeriodGroupReportLogicBase>().Union(EnumerateOccasionalIncomesGroups().Cast<PeriodGroupReportLogicBase>());
        }

        public IEnumerable<PeriodGroupReportLogicBase> EnumerateSpentsGroups()
        {
            return EnumerateStableSpentsGroups().Cast<PeriodGroupReportLogicBase>().Union(EnumerateOccasionalSpentsGroups().Cast<PeriodGroupReportLogicBase>());
        }

        public IDictionary<PeriodGroupReportLogicBase, double> EvaluateRelativeAmount(IEnumerable<PeriodGroupReportLogicBase> groups)
        {
            var groupsWithAmount = groups.Select(g => new
            {
                Group = g,
                Amount = g.GetAmount()
            }).ToArray();
            decimal totalAmount = groupsWithAmount.Select(g => g.Amount).Sum();
            return groupsWithAmount.ToDictionary(g => g.Group, g => (double)g.Amount / (double)totalAmount);
        }

        private static DateTime GetStartDate(DateTime commonStart, DateTime commonFinish, TimeSpan period, int index)
        {
            return commonStart + TimeSpan.FromDays(index * period.TotalDays);
        }

        private static DateTime GetFinishDate(DateTime commonStart, DateTime commonFinish, TimeSpan period, int index)
        {
            return commonStart + TimeSpan.FromDays((index + 1) * period.TotalDays);
        }

        private IEnumerable<PeriodStableGroupReportLogic> EnumerateStableIncomesGroups()
        {
            return _upperLogic.UpperLogic.Incomes.StableItems.GroupBy(i => i.Model.Name).Select(g => new PeriodStableGroupReportLogic(g.Key, _upperLogic.UpperLogic.Model.Incomes.Sign, g, _logicExtender, this));
        }

        private IEnumerable<PeriodOccasionalGroupReportLogic> EnumerateOccasionalIncomesGroups()
        {
            return _upperLogic.UpperLogic.Incomes.OccasionalItems.Where(i => _model.StartDate <= i.Model.Time && _model.FinishDate >= i.Model.Time).GroupBy(i => i.Model.Name).Select(g => new PeriodOccasionalGroupReportLogic(g.Key, _upperLogic.UpperLogic.Model.Incomes.Sign, g, _logicExtender, this));
        }

        private IEnumerable<PeriodStableGroupReportLogic> EnumerateStableSpentsGroups()
        {
            return _upperLogic.UpperLogic.Spents.StableItems.GroupBy(i => i.Model.Name).Select(g => new PeriodStableGroupReportLogic(g.Key, _upperLogic.UpperLogic.Model.Spents.Sign, g, _logicExtender, this));
        }

        private IEnumerable<PeriodOccasionalGroupReportLogic> EnumerateOccasionalSpentsGroups()
        {
            return _upperLogic.UpperLogic.Spents.OccasionalItems.Where(i => _model.StartDate <= i.Model.Time && _model.FinishDate >= i.Model.Time).GroupBy(i => i.Model.Name).Select(g => new PeriodOccasionalGroupReportLogic(g.Key, _upperLogic.UpperLogic.Model.Spents.Sign, g, _logicExtender, this));
        }
    }
}
